<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vue2与Vue3的区别</title>
</head>
<body>
  
</body>
<script>
  // Vue3.x出来以后跟Vue2.x有很多写法上面的差异差异，最开始的创建就有一定的不同了，
  // 以前Vue2.x通过new Vue()，手动生成,并且全局API挂在Vue上，如下：
  // vue2 
  import Vue from 'vue';
  import App from './App.vue';
  Vue.component('loading', {})
  new Vue({
    render: h => h(App)
  }).$mount('#app')

  //  现在Vue3.x通过createApp实例化，并且全局API挂在vue实例上 app，如下:
  // vue3
  import { createApp } from 'vue'
  import ElementPlus from 'element-plus'
  import * as ElIconModules from "@element-plus/icons-vue";
  import 'element-plus/dist/index.css'
  import App from './App.vue'
  import router from './router'
  import store from './store'
  const app = createApp(App)
  app.use(store)
  app.use(ElementPlus)
  app.use(router)
  app.component('loading', {})
  Object.keys(ElIconModules).forEach(function (key) {
      app.component(ElIconModules[key].name, ElIconModules[key]);
  });
  app.mount('#app')

  // 以下还有一些差异
  // 1. 生命周期钩子
  // ① 2.x
  export default {
    name: '',
    data() {
      return {}
    },
    components: {},
    methods: {},
    computed: {},
    beforeCreate() {
      console.log('beforeCreate 创建vue实例完成之前所执行的')
    },
    created() {
      console.log('created 实例创建完成之后的,初始化data和methods')
    },
    beforeMount() {
      console.log('beforeMount 开始编译把data里面的数据和vue语法生成html模板')
    },
    mounted() {
      console.log('mounted 开始挂载编译生成的html渲染到页面')
    },
    beforeUpdate() {
      console.log('beforeUpdate 当data里面数据改变时触发')
    },
    updated() {
      console.log('updated data里面的数据和页面保持同步')
    },
    beforeDestroy() {
      console.log('beforeDestroy vue实例从运行状态进入销毁状态,data、methods、指令等还处于可用状态')
    },
    destroyed() {
      console.log('destroyed 组件完全被销毁,data、methods、指令等已经不可用了')
    }
  }

  // ② 3.x
  export default defineComponent({
    name: "ed",
    props: {
      title: {
        type: String,
        default: "",
      },
    },
    //setup取消了beforeCreate和created在beforeCreate和created之前执行
    setup(porps, { emit }) {
      onBeforeMount(() => {
        console.log("onBeforeMount - 组件挂载到节点上之前执行的函数");
      });
      onMounted(() => {
        console.log("onMounted - 组件挂载完成后执行的函数");
      });
      onBeforeUpdate(() => {
        console.log("onBeforeUpdate - 组件更新之前执行的函数");
      });
      onUpdated(() => {
        console.log("onUpdated - 组件更新完成之后执行的函数");
      });
      onBeforeUnmount(() => {
        console.log("onBeforeUnmount - 组件卸载之前执行的函数");
      });
      onUnmounted(() => {
        console.log("onUnmounted - 组件卸载完成后执行的函数");
      });
      const state = reactive({
        //数据
        username: "",
        password: "",
        lowerCaseUsername: computed(() => state.username.toLowerCase()), //计算属性
      });
      //方法
      const fn= () => {};
      return {
        fn,
        onMounted,
        ...toRefs(state),
      };
    },
    computed: {},
    components: {},
    data() {
      return {};
    },
    methods: {},
  });

  // 以上可以看出vue2.0和3.0差异
  // Vue2--------------Vue3
  // beforeCreate----setup()
  // created----setup()
  // beforeMount----onBeforeMount
  // mounted----onMounted
  // beforeUpdate---- onBeforeUpdate
  // updated ---- onUpdated
  // beforeDestroy ---- onBeforeUnmount
  // destroyed ---- onUnmounted
  // 并且还可以看出vue2.0中响应性数据一般都在data,computed，方法基本都是局限在methods中使用，而3.0响应性数据正常可以通过引入的reactive，把数据放入reactive中，通过setup()方法来返回我们的响应性数据，函数可以直接放在setup中并return出去，这使代码会更加简便和整洁


  // 2.双向数据绑定原理
  // Vue2.x 基于Object.definePropert()对数据进行劫持 结合 发布订阅模式的方式来实现的。 Vue3.x基于Proxy来做数据大劫持代理，可以原生支持到数组的响应式，不需要重写数组的原型，还可以直接支持新增和删除属性，比Vue2.x的Object.defineProperty更加的清晰明了。
  // proxy和definePropert差异：
  // proxy相比于definePropert，defineProperty只能监听某个属性，不能对全对象监听，proxy可以省去for in、闭包等内容来提升效率(直接绑定整个对象即可),也可以监听数组，不用再去单独的对数组做特异性操作 vue3.x可以检测到数组内部数据的变化，但是proxy无法兼容全部浏览器，无法polyfill

  // 3.支持碎片
  // vue2只能有一个节点
  <template>
    <div class="form">
      <h3></h3>
    </div>
  </template>

  // vue3可以有多个节点
  <template>
    <div class="form"></div>
    <h3></h3>
  </template>

  // 4.取消filter
  // 2.x经常在组件中可以使用filter，但在 3.x 中，过滤器已移除，且不再支持。建议可以使函数方法调用或计算属性来替换它们。

  // 5. 函数组件写法改变 - h
  // 在 2.x 中，render 函数会自动接收 h 函数当一个参数
  // vue2
  Vue.component('loading', {
    render(h) {
      return h('div', {title: ''}, [''])
    },
  })

  // 在 3.x 中，h 函数现在是全局通过vue导入的，而不是作为参数自动传递。
  // vue3
  import { h } from 'vue'
  app.component('loading', {
    render(props, context) {
      return h('div', {title: ''}, [''])
    },
  })

  // 6. router、vuex、refs的以及nextTick使用
  // vue2
  <div ref="popover"></div>

  import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
  // 字符串
  this.$router.push('home')
  // 对象
  this.$router.push({path: '/login?url=' + this.$route.path});
  // 命名的路由
  this.$router.push({ name: 'user', params: { userId: 123 }})
  // 带查询参数，变成/backend/order?selected=2
  this.$router.push({path: '/backend/order', query: {selected: "2"}});
  //访问store属性下state下面的name
  this.$store.state.name
  //访问store里的user.js的login方法，从而要更新。
  this.$store.dispatch('Login', this.loginForm)
  //
  this.$nextTick(() => {
    console.log(this.$refs.popover)
  });
  methods: {
    ...mapMutations({add:'JIA',jf:'JIAN'}),
    ...mapActions(['jadd','delayAdd'])
  },

  // vue3
  <div ref="popover"></div>

  import{ nextTick , ref} from "vue";
  import { useRouter } from "vue-router";
  import { mapGetters } from "vuex";
  import { useStore } from "vuex";
  export default defineComponent({
    setup() {
      const popover= ref(null);
      const router = useRouter();
      const store = useStore();
      const state = reactive({
        urls: "",
        user: '',
      });

      const toggleSideBar = () => {
        store.dispatch("app/toggleSideBar");
      };
      const goTO = () => {
        router.push({ path: "/user/info" });
      };
      onMounted(() => {
        nextTick(() => {
          console.log(popover.value)
        })
      });
      return {
        toggleSideBar,
        ...toRefs(state),
        onMounted,
        goTO,
        popover
      };
    },
    computed: {
      ...mapGetters(["sidebar", "avatar", "device", "name"]),
    },
    components: { },
    data() {
      return {};
    },
  });

  // 7.computed和watch区别
  //vue2
  watch:{
  'form.minimumChargeAmount': {
      handler(val) {
        console.log(val)
      }
    },
    name(newValue, oldValue){
        console.log("修改了name", newValue, oldValue);
    },
    result: {
      immediate: true,
      deep: true,
      handler(newValue, oldValue) {
        console.log("修改了result", newValue, oldValue);
      },
    },
  },
  computed: {
    totalPage: function () {
      return Math.ceil(this.total / this.pageSize);
    },
    fullName: {
      get() {
        return this.firstName + "-" + this.lastName;
      },
      set(val) {
        const nameArr = val.split("-");
        this.firstName = nameArr[0];
        this.lastName = nameArr[1];
      },
    },
  }

  //vue3
  import {
    defineComponent,
    reactive,
    computed,
    toRefs,
    watch,
    ref,
    watchEffect,
  } from "vue";
  export default defineComponent({
    name: "",
    setup() {
      const state = reactive({
        //数据
        username: "",
        password: "",
        lowerCaseUsername: computed(() => state.username.toLowerCase()), //计算属性
        demo: "demo",
        name1: "1",
        name2: "2",
      });
      let name = computed(() => state.name1 + "-" + state.name2);
      let sure = computed({
        get() {
          return state.name1 + "-" + state.name2;
        },
        set(val) {
          const nameArr = val.split("-");
          state.name1 = nameArr[0];
          state.name2 = nameArr[1];
        },
      });
      //监听指定对象下面的属性
      // watch(
      //   () => state.demo,
      //   (newValue, oldValue) => {
      //     console.log("state.demo", newValue, oldValue);
      //   },
      //   {
      //     immediate: true,
      //   }
      // );
      let sum = ref(0);
      let msg = ref("watch使用");
      // 监视ref所定义的多个响应式数据
      watch(
        [sum, msg],
        ([s, m], [olds, oldm]) => {
          console.log("sum/msg变化", s, m, olds, oldm);
        },
        { immediate: true }
      );

      watchEffect(() => {
        console.log("监听state.demo", state.demo);
      });
      return {
        ...toRefs(state),
        name,
        sure,
        sum,
        msg,
      };
    },
    components: {},
    data() {
      return {};
    },
  });








</script>
</html>